import socket
import struct
import sched
import time
import redis
import json
import logging

from logging.handlers import RotatingFileHandler
from threading import Thread
from Unpack_Content import Unpack_Content
from concurrent.futures import ThreadPoolExecutor
from config import Config
url = Config.url
port = Config.port

fh = RotatingFileHandler(filename='ISCS.log',encoding='utf-8',maxBytes=3000*1024,backupCount=2)
logging.basicConfig(
            format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S',
            handlers=[fh, ],
            level=logging.DEBUG
        )

unpack_content = Unpack_Content()
# from Inversion import Inversion
# Inversion.inversion
# Inversion.reversal

# 连接redis
pool = redis.ConnectionPool(
                host='119.29.26.75',
                port=20004,
                db=0,
                password='App123456',
                # decode_responses=True
)
rs = redis.Redis(connection_pool=pool)
pipe = rs.pipeline(transaction=True)

pipe.get('basement_frame')
# 底层帧格式所占字节
basement_frame = json.loads(str(pipe.execute()[-1], encoding='utf-8'))


client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((url, port))
# 心跳
PANT = b'0xEF0xEF0x010x010x06000x010x01000xffffffff0xFD0xFD'
# 供电臂
SUPPLY_ELECTRICITY = b'0xEF0xEF0x010x010x07000x010x08000x01000x06000x010xFD0xFD'
# 首末班申请信息
first_last_train_apply = b'0X00000000'
# 是否接收到来自服务端的心跳
ATS_pant = False
# 是否已经发送过，每次启动时都需要发送一次心跳
ISCS_send_pant = False
# 记录每次获得数据的时间，没收到数据后每10秒发送一次心跳，超过30秒重新连接
GetData_Time = int(time.time())
content_all = dict()
for i in range(1,8):
    content_all[i] = list()
# print(content_all)


# 将低位在前的16进制转化为高位在前，并转化成10进制
def inversion(data):
    # data = data #type:str
    # 先将bytes数据转换成str
    # print('data', data)
    if isinstance(data, bytes):
        data = str(data, encoding='utf-8')
    # 切割
    val = data.split('x')[-1]
    val_list = list()
    for i in range(0,len(val),2):
        val_list.append(val[i:i+2:1])
    new_val_list = val_list[::-1]
    new_data = ''.join(new_val_list)
    new_data = '0x' + new_data
    return int(new_data, 16)


# 将低位在前变为高位在前
def reversal(data):
    # data = data #type:str
    # 先将bytes数据转换成str
    if isinstance(data,bytes):
        data=str(data, encoding='utf-8')
    # 切割
    val=data.split('x')[-1]
    val_list=list()
    for i in range(0, len(val), 2):
        val_list.append(val[i:i + 2:1])
    new_val_list=val_list[::-1]
    new_data=''.join(new_val_list)
    new_data='0x' + new_data
    return new_data


# 发送数据到服务端
def send_msg(msg):
    # print(msg)
    global client, ATS_pant, ISCS_send_pant

    send_len = struct.pack('i', len(msg))
    # try:
    # 为了解决黏包现象，先将数据的长度信息发送给客户端
    try:
        client.send(send_len)
        client.send(msg)
    except:
        # 断开已有连接
        try:
            client.close()
            # 如果发送出现错误重新建立连接
            client=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            client.connect((url, port))
            print(client)
        except:
            pass


# 发送心跳
def send_pant(pant):
    global ATS_pant, ISCS_send_pant, SUPPLY_ELECTRICITY, client
    scheduler = sched.scheduler(time.time, time.sleep)
    # time.sleep(0.1)
    while True:
        # time.sleep(0.1)
        # 当前时间
        Time = int(time.time())
        # 如果没有发送过心跳，则发送心跳到服务端
        if not ISCS_send_pant:
            # 发送心跳
            scheduler.enter(0, 1, send_msg, argument=(pant,))
            ISCS_send_pant = True
        # 如果没接收到信息的时长超过10秒但是没有超过30秒，重新发送心跳
        if Time - GetData_Time >= 10 and Time - GetData_Time < 30:
            scheduler.enter(0, 1 , send_msg, argument=(pant, ))
        # 如果没有接收到信息的时长超过30秒,重新建立连接
        elif Time - GetData_Time >= 30:
            # 断开已有连接
            try:
                client.close()
                client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                client.connect((url, port))
                # 重新建立连接后重新发送心跳
                scheduler.enter(0, 1, send_msg, argument=(pant,))
                # 表示已经发送过心跳
                ISCS_send_pant=True
            except:
                continue
            # ATS_pant = False
        scheduler.run()

        # if ATS_pant:
        #     # 发送供电臂
        #     scheduler.enter(10, 1, send_msg, argument=(SUPPLY_ELECTRICITY, ))
        # scheduler.run()


# def send_supply_electricity():
#     scheduler=sched.scheduler(time.time, time.sleep)
#     while True:
#         print(SUPPLY_ELECTRICITY)
#         scheduler.enter(5, 1, send_msg, argument=(SUPPLY_ELECTRICITY, ))
#         scheduler.run()


# 发送首末班申请
def send_apply():
    scheduler = sched.scheduler(time.time, time.sleep)
    while True:
        # 每隔5分钟发送一次首末班申请
        scheduler.enter(5, 1, send_msg, argument=(first_last_train_apply,))
        scheduler.run()

# 接收服务端数据
def receive():
    global ATS_pant, client, ISCS_send_pant, GetData_Time
    while True:
        try:
            length = client.recv(4)
            # 信息长度
            length = struct.unpack('i', length)[0]
            # 接收服务端发送过来的信息
            msg = client.recv(length)
            # logging.debug(msg)
            GetData_Time=int(time.time())
        except:
            ISCS_send_pant = False
            continue
        # 记录每次接收到数据的时间
        # GetData_Time = int(time.time())
        # print(msg)
        # 数据切片的位置
        position=0
        # 用于存放数据依据底层帧拆分后的结果
        ATS_MSG=dict()
        # 遍历底层帧清单，按照底层帧格式初步的拆分数据
        for key, value in basement_frame.items():
            # if key == 'data_head':
            #     data_head = client_msg[]
            if value != 'n':
                # print(key)
                if key == 'data_head' or key == 'data_tail':
                    ATS_MSG[key]=msg[position:position + (2 * value + 4):1]
                    if key == 'data_head':
                        data_head = ATS_MSG.get(key)
                        # print(data_head)
                        try:
                            data_head = str(data_head, encoding='utf=8')
                        except:
                            continue
                        # 如果帧头错误直接跳过
                        if data_head != '0xEF0xEF' and data_head != '0xef0xef':
                            continue
                    else:
                        data_tail = ATS_MSG.get(key)
                        data_tail = str(data_tail)
                        # 如果帧尾错误直接跳过
                        if data_tail != '0xFD0xFD' and data_tail != '0xfd0xfd':
                            continue
                    position+=2 * value + 4
                else:
                    ATS_MSG[key] = msg[position:position + (2 * value + 2):1]
                    position += 2 * value + 2
            else:
                # print(key)
                # 取得主要内容部分
                content=msg[position:-8:1]
                ATS_MSG['content']=content
                # print(content)
                position+=len(content)
        try:
            Type = inversion(ATS_MSG.get('Type'))
        except:
            continue
        if not Type:
            continue
        total = ATS_MSG.get('total')
        index = ATS_MSG.get('index')
        # print(Type)
        # content_all[Type] = content_all.get(Type) + str(ATS_MSG.get('content'), encoding='utf-8')
        try:
            content_all[Type].append(str(ATS_MSG.get('content'), encoding='utf-8'))
        except:
            continue
        # 如果index == total
        # if len(content_all.get(Type)) != total:
        #     content_all[Type].clear()
        #     continue
        if index == total:
            content = ''
            for con in content_all[Type]:
                content += con
            # content = content_all.get(Type)
            cont = ''
            name = ''
            if Type == 1:
                # print(content)
                # content = str(ATS_MSG.get('content'),encoding='utf-8')
                if content == '0xffffffff':
                    # print('获取服务端心跳成功')
                    ATS_pant = True
                    # print('服务端心跳')
                    # print(f'服务端心跳信息{content}')
                    name = '服务端心跳信息'
                    cont = content
            elif Type == 2:
                # print('列车信息', content)
                name = '列车信息'
                cont = unpack_content.unpack_train(content)
                # print(f'列车信息{content}')
                # unpack_station(c)
            elif Type == 3:
                # print('计划信息',content)
                # print(f'计划信息{content}')
                # 调用解析的方法
                name = '计划信息'
                cont = unpack_content.unpack_plan(content)
            elif Type == 4:
                # print('首末班信息',content)
                # 调用解析的方法
                cont = unpack_content.unpack_first_last_train(content)
                name = '首末班信息'
                # print(f'首末班信息{content}')
            elif Type == 5:
                # print('车门隔离状态信息')
                # 调用解析的方法
                cont = unpack_content.unpack_door_isolation_status(content)
                name = '车门隔离状态信息'
                # print(f'车门隔离状态信息{content}')
            elif Type == 6:
                # print('站台门隔离状态信息')
                # print(f'站台门隔离状态信息{content}')
                # 调用解析的方法
                cont = unpack_content.unpack_platform_door(content)
                name = '站台门隔离状态信息'
                # logging.debug(cont)
            else:
                # print('站场表示信息', content)
                # print(unpack_station_yard_content(7, content))
                # 调用解析的方法
                con = unpack_content.unpack_station_yard_content(content)
                cont = dict()
                cont['设备数量'] = 0
                cont['设备列表'] = list()
                con_num = 0
                for c in con:
                    con_num += 1
                    cont['设备列表'].append(c)
                cont['设备数量'] = con_num
                # print(next(cont))
                # print(next(cont))
                name = '站场表示信息'
            print(name, cont)
            logging.debug(f'{name}-{cont}   原始报文:{msg}')
            content_all[Type].clear()

receive_t = Thread(target=receive)
send_pant_t = Thread(target=send_pant, args=(PANT,))

# send_supply_electricity_t = Thread(target=send_supply_electricity)
send_apply_t = Thread(target=send_apply)
# send_pant_t.start()


send_pant_t.start()
# send_supply_electricity_t.start()
send_apply_t.start()
receive_t.start()
send_pant_t.join()

# send_supply_electricity_t.join()
send_apply_t.join()
receive_t.join()